﻿
using System.Collections.Concurrent;
using System.Threading.Channels;

using FreeSql;

using GCC.Container.Abstracts;
using GCC.Container.LogProviders.DBModel;

namespace GCC.Container.LogProviders
{
    /// <summary>
    /// 容器日志服务
    /// </summary>
    public class GCCLoggerProvider : AbsGCCLoggerProvider
    {

        /// <summary>
        /// 日志实例
        /// </summary>
        private static GCCLoggerProvider? _instance;
        /// <summary>
        /// 日志的日志器
        /// </summary>
        private static AbsGCCLogger? _logger;

        /// <summary>
        /// 懒汉锁
        /// </summary>
        private static readonly string _instanceLocker = Guid.NewGuid().ToString("");

        /// <summary>
        /// 当发生了消息写入时,订阅这个来自己做实现
        /// </summary>
        public event Action<(string moduleName, string? scope, LogLevelEnum level, string text, string? eventID, Exception? exception)>? OnWriteMessage;

        /// <summary>
        /// 强制获取日志实例,很可能是空
        /// </summary>
        public static GCCLoggerProvider? Instance { get => _instance; }

        /// <summary>
        /// 数据库日志池
        /// </summary>
        private readonly Channel<LogInfo> _dbLogPool = Channel.CreateUnbounded<LogInfo>();

        /// <summary>
        /// 日志数据库
        /// </summary>
        private readonly IFreeSql? _logDB;

        /// <summary>
        /// 日志的实例缓存
        /// </summary>
        private readonly ConcurrentDictionary<string, AbsGCCLogger> _loggerPool = new();

        /// <summary>
        /// 文件日志对象
        /// </summary>
        private readonly FileLog? _fileLogInstance;

        /// <summary>
        /// 日志的配置信息
        /// </summary>
        private readonly LogOptions _options = new();

        /// <summary>
        /// 获取日志实例
        /// </summary>
        public static GCCLoggerProvider GetInstance(LogOptions options)
        {
            if (_instance == null)
            {
                lock (_instanceLocker)
                {
                    if (_instance is null)
                    {
                        _instance = new GCCLoggerProvider(options);
                        _logger = _instance.CreateLogger<GCCLoggerProvider>();
                    }
                }

            }
            return _instance;
        }

        /// <summary>
        /// 创建一个日志器
        /// </summary>
        /// <param name="categoryName">分类/模块名称</param>
        /// <returns></returns>
        public override AbsGCCLogger CreateLogger(string categoryName)
        {
            return _loggerPool.GetOrAdd(categoryName, (key) =>
            {
                return new LogCenter(categoryName, _options, _fileLogInstance);
            });

        }

        /// <summary>
        /// 根据类型创建一个日志器
        /// </summary> 
        /// <returns></returns>
        public override AbsGCCLogger CreateLogger<T>()
        {
            return CreateLogger(typeof(T).FullName ?? "Error type");
        }

        /// <summary>
        /// 创建一个局部日志器
        /// </summary>
        /// <param name="categoryName">分类/模块名称</param>
        /// <param name="scopeName">局部自定义名称</param>
        /// <returns></returns>
        public override AbsGCCLogger? CreateScopeLogger(string categoryName, string scopeName)
        {
            if (_options.FileLog is null)
            {
                return null;
            }
            var flInfo = new FileLog(new FileLogOptions() { FolderPath = _options.FileLog.FolderPath }, scopeName);
            return new LogCenter(categoryName, _options, flInfo, scopeName);

        }

        /// <summary>
        /// 根据类型创建一个局部日志器
        /// </summary> 
        /// <param name="scopeName">局部自定义名称</param>
        /// <returns></returns>
        public override AbsGCCLogger? CreateScopeLogger<T>(string scopeName)
        {
            return CreateScopeLogger(typeof(T).FullName ?? "Error type", scopeName);
        }

        /// <summary>
        /// 数据库日志任务
        /// </summary>
        private void DBLogJob()
        {
            if (_logDB is null)
            {
                return;
            }
            _ = Task.Factory.StartNew(async () =>
            {
                //var reader = _dbLogPool.Reader;
                //await foreach (var item in reader.ReadAllAsync())
                //{
                //    _ = _logDB?.Insert(item).ExecuteAffrows();
                //}

                var reader = _dbLogPool.Reader;
                while (true)
                {
                    var asd = await reader.WaitToReadAsync();
                    var count = reader.Count;
                    var items = new List<LogInfo>();
                    while (items.Count < count)
                    {
                        items.Add(await reader.ReadAsync());
                    }
                    try
                    {
                        _ = _logDB.Insert(items).ExecuteAffrows();
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"Save log data to db error.", exception: ex);
                        continue;
                    }
                }

            }, TaskCreationOptions.LongRunning);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options">日志参数</param>
        private GCCLoggerProvider(LogOptions options)
        {
            this._options = options;
            if (options.FileLog is not null)
            {
                _fileLogInstance = new FileLog(options.FileLog);
            }
            if (!string.IsNullOrWhiteSpace(_options.DBAddress))
            {
                _logDB = new FreeSqlBuilder().UseConnectionString(_options.DBType, _options.DBAddress).Build();
            }
            DBLogJob();
        }

        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="moduleName">模块名称</param>
        /// <param name="scope">范围</param>
        /// <param name="level">级别</param>
        /// <param name="text">文本</param>
        /// <param name="eventID">事件ID</param>
        /// <param name="exception">异常</param>
        internal void WriteLog(string moduleName, string? scope, LogLevelEnum level, string text, string? eventID, Exception? exception)
        {
            if (_logDB is not null)
            {
                _ = _dbLogPool.Writer.TryWrite(new LogInfo()
                {
                    Created = DateTime.Now,
                    EventID = eventID ?? "",
                    Exception = exception is null ? "" : exception.Message,
                    Level = (int)level,
                    ModuleName = moduleName,
                    Text = text,
                    Scope = scope ?? "",
                });
            }
            try
            {
                OnWriteMessage?.Invoke((moduleName, scope, level, text, eventID, exception));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Call OnWriteMessage error.Message:{ex.Message}");
            }
        }

#pragma warning disable CA1822 // Mark members as static
        public void Dispose()
#pragma warning restore CA1822 // Mark members as static
        {

        }
    }
}
